package top.codedance.iotp.simulator.serialPort;

import cd.io.netty.bootstrap.Bootstrap;
import cd.io.netty.buffer.ByteBuf;
import cd.io.netty.buffer.Unpooled;
import cd.io.netty.channel.*;
import cd.io.netty.channel.nio.NioEventLoopGroup;
import cd.io.netty.channel.socket.DatagramChannel;
import cd.io.netty.channel.socket.DatagramPacket;
import cd.io.netty.channel.socket.nio.NioDatagramChannel;
import top.codedance.iotp.common.util.BytesBuilder;
import org.apache.log4j.Logger;
import org.ini4j.Ini;

import java.net.InetSocketAddress;
import java.util.List;

public class UdpServer implements Runnable {
    private Logger logger = SimulatorApplication.getLogger(UdpServer.class);
    private int port = 10017;
    private ChannelFuture channelFuture = null;

    public UdpServer() {
    }

    @Override
    public void run() {
        if (SimulatorApplication.ini != null && SimulatorApplication.ini.containsKey("udp")) {
            Ini.Section section = SimulatorApplication.ini.get("udp");
            if (section.containsKey("port")) {
                port = Integer.parseInt(section.get("port"));
            }
            boot();
        }
    }

    private void boot() {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .handler(new ChannelInitializer<DatagramChannel>() {
                        @Override
                        protected void initChannel(DatagramChannel ch) throws Exception {
                            ch.pipeline().addLast(new SimpleChannelInboundHandler<DatagramPacket>() {
                                @Override
                                protected void messageReceived(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket) throws Exception {
                                    InetSocketAddress sender = datagramPacket.sender();
                                    String ip = sender.getAddress().getHostAddress();
                                    logger.debug("Reqeust IP: " + ip);
                                    ByteBuf buf = datagramPacket.copy().content();
                                    try {
                                        byte[] data = new byte[buf.readableBytes()];
                                        buf.readBytes(data);
                                        List<BytesBuilder> results = SimulatorApplication.uaimulator.exec(data);
                                        for (BytesBuilder bytesBuilder : results) {
                                            try {
                                                byte[] bytes = bytesBuilder.toArray();
                                                channelHandlerContext.channel().writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(bytes), datagramPacket.sender()));
                                                logger.debug(new String(bytes));
                                            } catch (Exception e) {
                                                logger.debug(e.getMessage());
                                            }
                                        }
                                        String dataStr = new String(data);
                                        logger.debug(dataStr);
                                    }catch (Exception e){
                                        logger.debug(e.getMessage());
                                    }
                                }
                            });
                        }
                    });
            channelFuture = bootstrap.bind(port).sync();
            logger.info("UDP server is running, port: [" + port + "]!");
            channelFuture.channel().closeFuture().await();
        } catch (Exception e) {
            logger.debug(e.getMessage());
        } finally {
            group.shutdownGracefully();
        }
    }

    public void close() {
        try {
            channelFuture.channel().closeFuture().await();
        } catch (Exception e) {
            logger.debug(e.getMessage());
        }
    }
}
